﻿(function() {
    
    // defines
    var STAGE_NO = 0;
    var STAGE_GROWUP = 1;
    var STAGE_FLOWER = 100;
    var STAGE_AFTER_HARVEST = 101;

    var FARM_BLOCK_BLOCKED = 0;
    var FARM_BLOCK_NO_PLANT = 1;
    var FARM_BLOCK_PLANT = 2;

    var FARM_BLOCK_MAX_WORMS = 3;
    var FARM_BLOCK_MAX_WEEDS = 3;

    var FARM_BLOCK_EVENT_DATA_CHANGE = "farm_block_event_data_change";
    var FARM_BLOCK_EVENT_NOTIFY_EXP_FLOATING = "farm_block_event_exp";
    var FARM_ACTION_EVENT = "farm_action";
    
    var FARM_ACTION_BUY = 0;
    var FARM_ACTION_SELL = 1;

    var FANDI_EXP = 5;
    var HARVEST_EXP = 15;
    var PLANT_EXP = 10;
    var KILL_WORM_EXP = 3;
    var KILL_WEED_EXP = 3;
    var WATER_EXP = 3;

    var __models = null;
    
    function __FarmEvent(farm, code, value)
    {
        if (this instanceof __FarmEvent)
        {
            this.farm = farm;
            this.actionCode = code;
            this.actionValue = value;
        }
        else
        {
            return new __FarmEvent(farm, code, value);
        }
    }

    function __FarmBlockExpEvent(obj, str, exp)
    {
        if (this instanceof __FarmBlockExpEvent)
        {
            this.listenObj = obj;
            this.exp = exp;
            this.str = str + " exp " + exp;
        }
        else
        {
            return new __FarmBlockExpEvent(obj, str, exp);
        }
    }

    function FarmBlock(farm)
    {
        if (this instanceof FarmBlock)
        {
            ListenObject.call(this);
            this.status = FARM_BLOCK_NO_PLANT;
            this.curStage = STAGE_NO;
            this.currentTime = 0;
            this.vegTotalNum = 0;
            this.vegCurNum = 0;
            this.curSeason = 0;
            this.vegName = "";
            this.veg = null;
            this.landDry = false;
            this.weeds = 0;
            this.pests = 0;
            this.lastGrowupStage = 0;
            this._farm = farm;
        }
        else
        {
            return new FarmBlock(farm);
        }
    }

    inheritPrototype(FarmBlock, ListenObject);

    FarmBlock.prototype.getStatus = function()
    {
        return this.status;
    }

    FarmBlock.prototype.getStage = function()
    {
        return this.curStage;
    }

    FarmBlock.prototype.getParentFarm = function()
    {
        return this._farm;
    }

    FarmBlock.prototype.getGrowupStage = function()
    {
        var isFirst = (this.curSeason == 0);
        var matureTime = this.veg.getMatureTime(isFirst);
        var totalGrowupStage = this.veg.numStage;
        if (!isFirst) 
            totalGrowupStage--;
        var growUpStage = Math.floor(this.currentTime * totalGrowupStage / matureTime);
        if (!isFirst)
            growUpStage++;
        if (growUpStage >= this.veg.numStage - 1)
        {
            growUpStage = this.veg.numStage - 1;
        }
        return growUpStage;
    }

    FarmBlock.prototype.timeNeedForNextStage = function()
    {
        var isFirst = (this.curSeason == 0);
        var matureTime = this.veg.getMatureTime(isFirst);
        var totalGrowupStage = this.veg.numStage;
        if (!isFirst) 
            totalGrowupStage--;
        var growUpStage = Math.floor(this.currentTime * totalGrowupStage / matureTime);
        if (growUpStage >= totalGrowupStage - 1)
        {
            return 0;
        }
        growUpStage++;
        return growUpStage * matureTime / totalGrowupStage - this.currentTime;
    }

    FarmBlock.prototype.fandi = function()
    {
        if (this.status == FARM_BLOCK_PLANT)
        {
            // should have dialog to warning. but this dialog should popup in views layer. 
            this.status = FARM_BLOCK_NO_PLANT;
            this.updateListeners(FARM_BLOCK_EVENT_DATA_CHANGE, this);
            if (this.curStage == STAGE_AFTER_HARVEST)
            {
                this.updateListeners(FARM_BLOCK_EVENT_NOTIFY_EXP_FLOATING, 
                    new __FarmBlockExpEvent(this, "fandi", FANDI_EXP));
            }	
        }
    }

    FarmBlock.prototype.canHarvest = function()
    {
        return this.status == FARM_BLOCK_PLANT && this.curStage == STAGE_FLOWER;
    }

    FarmBlock.prototype.harvest = function()
    {
        if (this.canHarvest())
        {
            // if need exp and gold here.
            // pass revelant listener event.
            var veg = this.veg;
            assert (veg.numSeason > this.curSeason, "veg.numSeason =" + veg.numSeason + "this.curSeason = " + this.curSeason);
            assert (this.vegCurNum > 0);
            __models.wareHouse.addVeg(veg.name, this.vegCurNum);
            this.updateListeners(FARM_BLOCK_EVENT_NOTIFY_EXP_FLOATING, 
                    new __FarmBlockExpEvent(this, "harvest", veg.harvestExp));
        
            this.vegCurNum = 0;
            this.vegTotalNum = 0;
            this.lastGrowupStage = 0;
            
            if (this.curSeason + 1 < veg.numSeason)
            {
                this.curSeason++;
                this.curStage = STAGE_GROWUP;
                this.vegTotalNum = veg.generateVegNum();
                this.vegCurNum = this.vegTotalNum;
                this.lastGrowupStage = 1;
            }
            else
            {
                this.curStage = STAGE_AFTER_HARVEST;
            }
            
            this.updateListeners(FARM_BLOCK_EVENT_DATA_CHANGE, this);
        }
    }

    FarmBlock.prototype.plant = function(name)
    {
        if (this.status == FARM_BLOCK_NO_PLANT)
        {
            this.vegName = name;
            this.veg = StaticModels.getVegStore()[name];
            this.status = FARM_BLOCK_PLANT;
            this.curStage = STAGE_GROWUP;
            this.curSeason = 0;
            this.vegTotalNum = this.veg.generateVegNum();
            this.vegCurNum = this.vegTotalNum;
            this.lastGrowupStage = 0;
            this.updateListeners(FARM_BLOCK_EVENT_DATA_CHANGE, this);
            this.updateListeners(FARM_BLOCK_EVENT_NOTIFY_EXP_FLOATING, 
                    new __FarmBlockExpEvent(this, "plant", PLANT_EXP));
        }
    }

    FarmBlock.prototype.killWeeds = function()
    {
        if (this.weeds > 0)
        {
            this.weeds--;
            this.updateListeners(FARM_BLOCK_EVENT_DATA_CHANGE, this);
            this.updateListeners(FARM_BLOCK_EVENT_NOTIFY_EXP_FLOATING, 
                    new __FarmBlockExpEvent(this, "kill weeds", KILL_WEED_EXP));
        }
    }

    FarmBlock.prototype.killWorms = function()
    {
        if (this.pests > 0)
        {
            this.pests--;
            this.updateListeners(FARM_BLOCK_EVENT_DATA_CHANGE, this);
            this.updateListeners(FARM_BLOCK_EVENT_NOTIFY_EXP_FLOATING, 
                    new __FarmBlockExpEvent(this, "kill worms", KILL_WORM_EXP));
        }
    }

    FarmBlock.prototype.water = function()
    {
        if (this.landDry)
        {
            this.landDry = false;
            this.updateListeners(FARM_BLOCK_EVENT_DATA_CHANGE, this);
            this.updateListeners(FARM_BLOCK_EVENT_NOTIFY_EXP_FLOATING, 
                    new __FarmBlockExpEvent(this, "water", WATER_EXP));
        }
    }

    FarmBlock.prototype.placeWorms = function()
    {
        if (this.pests < FARM_BLOCK_MAX_WORMS && this.status == FARM_BLOCK_PLANT)
        {
            this.pests++;
            this.updateListeners(FARM_BLOCK_EVENT_DATA_CHANGE, this);
        }
    }

    FarmBlock.prototype.update = function(interval)
    {
        this.localSimulateUpdate(interval);
    }

    FarmBlock.prototype.getTipText = function()
    {
        if (this.getStatus() == FARM_BLOCK_NO_PLANT)
        {
            return "plant something!";
        }
        else if (this.getStatus() == FARM_BLOCK_BLOCKED)
        {
            return "blocked, you need money to unblock it.";
        }
        else if (this.getStatus() == FARM_BLOCK_PLANT)
        {
            if (this.getStage() == STAGE_GROWUP)
            {
                return "veg is " + this.vegName + " growing up, current stage =" + Math.floor(this.getGrowupStage())
                    + " need " + Math.floor(this.timeNeedForNextStage()) + " seconds to next stage." + "asdfasdfasdfasdfadsf";
            }
            else if (this.getStage() == STAGE_FLOWER)
            {
                return "harvest plants! veg num =" + this.vegCurNum;
            }
            else if (this.getStage() == STAGE_AFTER_HARVEST)
            {
                return "clean the block.";
            }
            else
            {
                assert(false, "Blockview::highlight, should not go here.");
            }
        }
        return "";
    }

    FarmBlock.prototype.localSimulateUpdate = function(interval)
    {
        // just simple simulate. random weeds, worms, dry will need not interval. but growup need it.
        // random grow.
        var veg = this.veg;
        var needUpdate = false;
        if (this.status == FARM_BLOCK_PLANT)
        {
            if (this.curStage == STAGE_GROWUP)
            {
                this.currentTime += interval;
                if (this.currentTime > veg.getMatureTime(this.curSeason == 0))
                {
                    needUpdate = true;
                    this.curStage = STAGE_FLOWER;
                    this.currentTime = 0;
                    log.info("vegetable mature.");
                }
                else
                {
                    var curGrowupStage = this.getGrowupStage();
                    if (curGrowupStage > this.lastGrowupStage)
                    {
                        needUpdate = true;
                        this.lastGrowupStage = curGrowupStage;
                    }
                }
            }
        }
        
        // random bad things.
        var badthings = getRandInt(0, 1000);
        if (badthings < 2 && this.status == FARM_BLOCK_PLANT)
        {
            // some thing appear.
            if (this.landDry == false)
            {
                this.landDry = true;
                needUpdate = true;
            }
            else if (this.pests < FARM_BLOCK_MAX_WORMS)
            {
                this.pests++;
                needUpdate = true;
            }
            else if (this.weeds < FARM_BLOCK_MAX_WEEDS)
            {
                this.weeds++;
                needUpdate = true;
            }
        }
        if (needUpdate)
            this.updateListeners(FARM_BLOCK_EVENT_DATA_CHANGE, this);
    }

    function WareHouseData()
    {
        if (this instanceof WareHouseData)
        {
            ListenObject.call(this);
            this.seeds = {};
            this.vegs = {};
            this.others = {};	// like helper items.     
        }
        else
        {
            return new WareHouseData();
        }
    }
    inheritPrototype(WareHouseData, ListenObject);

    WareHouseData.prototype.addVeg = function(name, num)
    {
        if (!this.vegs[name])
        {
            this.vegs[name] = 0;
        }
        this.vegs[name] += num;
    }

    WareHouseData.prototype.addSeed = function(name, num)
    {
        if (!this.seeds[name])
        {
            this.seeds[name] = 0;
        }
        this.seeds[name] += num;
    }
    
    var FARMDATA_ROW_NUM = 3;
    var FARMDATA_COL_NUM = 5;
    var FARMDATA_TOTOL_NUM = FARMDATA_ROW_NUM * FARMDATA_COL_NUM;

    var FARM_DATA_EVENT_USER_STATUS = "farm_data_user_status";

    function FarmData()
    {
        if (this instanceof FarmData)
        {
            ListenObject.call(this);
            this.blocks = new Array(FARMDATA_TOTOL_NUM);
            for (var i = 0; i < FARMDATA_TOTOL_NUM; i++)
            {
                this.blocks[i] = new FarmBlock(this);
                this.blocks[i].registerListener(FARM_BLOCK_EVENT_NOTIFY_EXP_FLOATING, bind(this.addExp, this));
            }
            this.exp = 0;
            this.gold = 10000;
        }
        else
        {
            return FarmData();
        }
    }

    inheritPrototype(FarmData, ListenObject);

    FarmData.prototype.harvestAll = function()
    {
        for (var i = 0; i < FARMDATA_TOTOL_NUM; i++)
        {
            this.blocks[i].harvest();
        }
    }

    FarmData.prototype.update = function(interval)
    {
        for (var i = 0; i < FARMDATA_TOTOL_NUM; i++)
        {
            this.blocks[i].update(interval);
        }
    }

    FarmData.prototype.addExp = function(e)
    {
        this.exp += e.exp;
        this.updateListeners(FARM_DATA_EVENT_USER_STATUS, this);
    }

    FarmData.prototype.getExp = function()
    {
        return this.exp;
    }

    function UserData(name)
    {
        if (this instanceof UserData)
        {
            this.name = name;
        }
        else
        {
            return UserData(name);
        }
    }

    function InternalModels() 
    {
        if (this instanceof InternalModels)
        {
            ListenObject.call(this);
            this.farm = new FarmData();
            this.wareHouse = new WareHouseData();
            this.friendlist = null;
            this.userData = new UserData("lining");
        }
        else
        {
            return new InternalModels();
        }
    }

    inheritPrototype(InternalModels, ListenObject);

    InternalModels.prototype.update = function(interval)
    {
        this.farm.update(interval);
    }
    
    InternalModels.prototype.getFarm = function()
    {
        return this.farm;
    }

    InternalModels.prototype.getWareHouse = function()
    {
        return this.wareHouse;
    }
    
    InternalModels.prototype.getUserData = function()
    {
        return this.userData;
    }
    
    InternalModels.prototype.buySeed = function(vegName, num)
    {
        var veg = StaticModels.getVegStore()[vegName];
        var moneyNeed = veg.seedPrice * num;
        var boughtValue = null;
        if (this.farm.gold >= moneyNeed)
        {
            boughtValue = {'name' : vegName, 'num' : num};
            this.farm.gold -= moneyNeed;
            this.wareHouse.addSeed(vegName, num);
        }
        this.updateListeners(FARM_ACTION_EVENT, 
                                 new __FarmEvent(this.farm, FARM_ACTION_BUY, boughtValue));
    }

    InternalModels.prototype.sellVeg = function(vegName, num)
    {
        assert(num > 0, "only can sell > 0 veg num. but num = " + num);
        var veg = StaticModels.getVegStore()[vegName];
        var moneyGot = veg.price * num;
        var sellValue = null;
        if (this.wareHouse.vegs[vegName] && this.wareHouse.vegs[vegName] >= num)
        {
            this.farm.gold += moneyGot;
            this.wareHouse.addVeg(vegName, -num);
            sellValue = {'name' : vegName, 'num' : num};
        }
        this.updateListeners(FARM_ACTION_EVENT,
                                 new __FarmEvent(this.farm, FARM_ACTION_SELL, sellValue));
    }
    
    Models = new Object();
    
    // export events
    Models.Event = new Object();
    Models.Event.User = new Object();
    Models.Event.User.STATUS = FARM_DATA_EVENT_USER_STATUS;
    Models.Event.FarmBlock = new Object();
    Models.Event.FarmBlock.UPDATE = FARM_BLOCK_EVENT_DATA_CHANGE;
    Models.Event.FarmBlock.EXP_UPDATE = FARM_BLOCK_EVENT_NOTIFY_EXP_FLOATING;
    Models.Event.Farm = new Object();
    Models.Event.Farm.ACTION =FARM_ACTION_EVENT;
    
    Models.FarmStage = new Object();
    Models.FarmStage.NO = STAGE_NO;
    Models.FarmStage.GROWUP = STAGE_GROWUP;
    Models.FarmStage.FLOWER = STAGE_FLOWER;
    Models.FarmStage.AFTER_HARVEST = STAGE_AFTER_HARVEST;

    Models.FarmBlock = new Object();
    Models.FarmBlock.BLOCKED = FARM_BLOCK_BLOCKED;
    Models.FarmBlock.NO_PLANT = FARM_BLOCK_NO_PLANT;
    Models.FarmBlock.PLANT = FARM_BLOCK_PLANT;

    Models.Farm = new Object();
    Models.Farm.ROW = FARMDATA_ROW_NUM;
    Models.Farm.COL = FARMDATA_COL_NUM;
    Models.Farm.TOTAL = FARMDATA_ROW_NUM * FARMDATA_COL_NUM;
    
    Models.Farm.ACTION_CODE = new Object();
    Models.Farm.ACTION_CODE.BUY = FARM_ACTION_BUY;
    Models.Farm.ACTION_CODE.SELL = FARM_ACTION_SELL;
    
    Models.createModels = function() {
        assert(__models == null, "cant create global models twice.");
        __models = new InternalModels();
        
        Models.getModels = function() {return __models;}
        Models.createModels = null;
    }
    
    Models.getShopItemList = function(cb)
    {
        var _ret = [];
        for (var i = 0; i < 20; i++)
        {
            _ret.push("veg" + i);
        }
        cb(_ret);
    }

    Models.getWarehouseItemList = function(cb)
    {
        return __models.wareHouse.vegs;
    }
})();